package com.springboot.fram.core.file;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.image.*;
import java.io.File;
import java.io.IOException;

/**
 * 图片处理工具类
 * <pre>
 * 功能：缩放图像、切割图像、图像类型转换、彩色转黑白、文字水印、图片水印等
 * JPEG
 * FIXME 需要改为web下能使用的output模式
 * </pre>
 *
 * @author madman
 */
public final class ImageUtils {

  private ImageUtils() {
  }

  /**
   * 等比例缩放图像
   *
   * @param srcImageFile 源图像文件
   * @param destImageFile 缩放后的图像文件
   * @param scale 缩放比例
   */
  public static void scale(File srcImageFile, File destImageFile, double scale) {
    try {
      BufferedImage src = ImageIO.read(srcImageFile);
      int width = src.getWidth();
      int height = src.getHeight();
      width = (int) (width * scale);
      height = (int) (height * scale);
      Image image = src.getScaledInstance(width, height, Image.SCALE_DEFAULT);
      BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
      Graphics g = tag.getGraphics();
      g.drawImage(image, 0, 0, null);
      g.dispose();
      ImageIO.write(tag, ImageType.JPEG.name(), destImageFile);
    } catch (IOException e) {
      throw new ImageException("等比例缩放图片异常", e);
    }
  }

  /**
   * 缩放图像（按高度和宽度缩放）
   *
   * @param srcImageFile 源图像文件地址
   * @param destImageFile 缩放后的图像地址
   * @param width 缩放后的宽度
   * @param height 缩放后的高度
   * @param fixedColor 比例不对时补充的颜色，不补充为<code>null</code>
   */
  public static void scale(File srcImageFile, File destImageFile, int width, int height,
      Color fixedColor) {
    try {
      double ratio;
      BufferedImage bi = ImageIO.read(srcImageFile);
      Image item = bi.getScaledInstance(width, height, BufferedImage.SCALE_SMOOTH);
      if ((bi.getHeight() > height) || (bi.getWidth() > width)) {
        double v = Integer.valueOf(height).doubleValue();
        if (bi.getHeight() > bi.getWidth()) {
          ratio = v / bi.getHeight();
        } else {
          ratio = v / bi.getWidth();
        }
        AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio),
            null);
        item = op.filter(bi, null);
      }
      if (null != fixedColor) {
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();
        g.setColor(fixedColor);
        g.fillRect(0, 0, width, height);
        if (width == item.getWidth(null)) {
          g.drawImage(item, 0, (height - item.getHeight(null)) / 2, item.getWidth(null),
              item.getHeight(null),
              fixedColor, null);
        } else {
          g.drawImage(item, (width - item.getWidth(null)) / 2, 0, item.getWidth(null),
              item.getHeight(null), fixedColor,
              null);
        }
        g.dispose();
        item = image;
      }
      ImageIO.write((BufferedImage) item, ImageType.JPEG.name(), destImageFile);
    } catch (IOException e) {
      throw new ImageException("宽高缩放图片异常", e);
    }
  }

  /**
   * 图像切割(按指定起点坐标和宽高切割)
   *
   * @param srcImageFile 源图像地址
   * @param destImageFile 切片后的图像地址
   * @param startX 目标切片起点坐标X
   * @param startY 目标切片起点坐标Y
   * @param width 目标切片宽度
   * @param height 目标切片高度
   */
  public static void cut(File srcImageFile, File destImageFile, int startX, int startY, int width,
      int height) {
    try {
      BufferedImage bi = ImageIO.read(srcImageFile);
      int srcWidth = bi.getHeight();
      int srcHeight = bi.getWidth();
      Image image = bi.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
      ImageFilter cropFilter = new CropImageFilter(startX, startY, width, height);
      Image img = Toolkit.getDefaultToolkit()
          .createImage(new FilteredImageSource(image.getSource(), cropFilter));
      BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
      Graphics g = tag.getGraphics();
      g.drawImage(img, 0, 0, width, height, null);
      g.dispose();
      ImageIO.write(tag, ImageType.JPEG.name(), destImageFile);
    } catch (IOException e) {
      throw new ImageException("按指定起点坐标和宽高切割图片异常", e);
    }
  }

  /**
   * 图像切割（指定切片的宽度和高度）
   *
   * @param srcImageFile 源图像地址
   * @param descDir 切片目标文件夹
   * @param destWidth 目标切片宽度
   * @param destHeight 目标切片高度
   */
  public static void cut(String srcImageFile, String descDir, int destWidth, int destHeight) {
    File srcFile = new File(srcImageFile);
    File destDir = new File(descDir);
    if (!srcFile.exists() && !destDir.mkdir()) {
      throw new ImageException("切割图片生成文件夹失败");
    }
    try {
      BufferedImage bi = ImageIO.read(srcFile);
      int srcWidth = bi.getHeight();
      int srcHeight = bi.getWidth();
      Image image = bi.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
      int cols;
      int rows;
      if (srcWidth % destWidth == 0) {
        cols = srcWidth / destWidth;
      } else {
        cols = (int) Math.floor(srcWidth / destWidth) + 1;
      }
      if (srcHeight % destHeight == 0) {
        rows = srcHeight / destHeight;
      } else {
        rows = (int) Math.floor(srcHeight / destHeight) + 1;
      }
      for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
          ImageFilter cropFilter = new CropImageFilter(j * destWidth, i * destHeight, destWidth,
              destHeight);
          Image img = Toolkit.getDefaultToolkit()
              .createImage(new FilteredImageSource(image.getSource(), cropFilter));
          BufferedImage tag = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB);
          Graphics g = tag.getGraphics();
          g.drawImage(img, 0, 0, null);
          g.dispose();
          ImageIO.write(tag, "JPEG", new File(descDir, "r" + i + "_c" + j + ".jpg"));
        }
      }
    } catch (IOException e) {
      throw new ImageException("切割图片失败", e);
    }

  }

  /**
   * 图像切割（指定切片的行数和列数）
   *
   * @param srcImageFile 源图像地址
   * @param descDir 切片目标文件夹
   * @param rows 目标切片行数
   * @param cols 目标切片列数
   */
  public static void cutByRowsAndCols(File srcImageFile, File descDir, int rows, int cols) {
    if (!descDir.exists() && !descDir.mkdir()) {
      throw new ImageException("切割图片生成文件夹失败");
    }
    // 读取源图像
    try {
      BufferedImage bi = ImageIO.read(srcImageFile);
      int srcWidth = bi.getHeight();
      int srcHeight = bi.getWidth();
      Image img;
      ImageFilter cropFilter;
      Image image = bi.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
      int destWidth;
      int destHeight;
      if (srcWidth % cols == 0) {
        destWidth = srcWidth / cols;
      } else {
        destWidth = (int) Math.floor(srcWidth / cols) + 1;
      }
      if (srcHeight % rows == 0) {
        destHeight = srcHeight / rows;
      } else {
        destHeight = (int) Math.floor(srcWidth / rows) + 1;
      }
      for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
          cropFilter = new CropImageFilter(j * destWidth, i * destHeight, destWidth, destHeight);
          img = Toolkit.getDefaultToolkit()
              .createImage(new FilteredImageSource(image.getSource(), cropFilter));
          BufferedImage tag = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB);
          Graphics g = tag.getGraphics();
          g.drawImage(img, 0, 0, null);
          g.dispose();
          ImageIO.write(tag, ImageType.JPEG.name(), new File(descDir, "r" + i + "_c" + j + ".jpg"));
        }
      }
    } catch (IOException e) {
      throw new ImageException("切割图片成行列失败", e);
    }

  }

  /**
   * 图像类型转换：GIF->JPG、GIF->PNG、PNG->JPG、PNG->GIF(X)、BMP->PNG 可以考虑枚举
   *
   * @param srcImageFile 源图像文件
   * @param destImageFile 目标图像文件
   * @param type 图片枚举
   */
  public static void convert(File srcImageFile, File destImageFile, ImageType type) {
    try {
      BufferedImage src = ImageIO.read(srcImageFile);
      ImageIO.write(src, type.name(), destImageFile);
    } catch (IOException e) {
      throw new ImageException("转换图片格式失败", e);
    }
  }

  /**
   * 彩色转为黑白
   *
   * @param srcImageFile 源图像地址
   * @param destImageFile 目标图像地址
   */
  public static void gray(File srcImageFile, File destImageFile) {
    try {
      BufferedImage src = ImageIO.read(srcImageFile);
      ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
      ColorConvertOp op = new ColorConvertOp(cs, null);
      src = op.filter(src, null);
      ImageIO.write(src, ImageType.JPEG.name(), destImageFile);
    } catch (IOException e) {
      throw new ImageException("图片转换为黑白失败", e);
    }

  }

  /**
   * @param text 水印文字
   * @param srcImageFile 源图像地址
   * @param destImageFile 目标图像地址
   * @param x 修正值
   * @param font 字体
   * @param color 颜色
   * @param alpha 透明度
   */
  public static void addWaterMark(String text, File srcImageFile, File destImageFile, int x, int y,
      Font font,
      Color color, float alpha) {
    try {
      Image src = ImageIO.read(srcImageFile);
      int width = src.getWidth(null);
      int height = src.getHeight(null);
      BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
      Graphics2D g = image.createGraphics();
      g.drawImage(src, 0, 0, width, height, null);
      g.setColor(color);
      g.setFont(font);
      int fontSize = font.getSize();
      g.drawString(text, (width - (getTextLength(text) * fontSize)) / 2 + x,
          (height - fontSize) / 2 + y);
      g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
      g.dispose();
      ImageIO.write(image, ImageType.JPEG.name(), destImageFile);
    } catch (IOException e) {
      throw new ImageException("图片添加水印文字失败", e);
    }
  }

  /**
   * 给图片添加图片水印
   *
   * @param waterMark 水印图片
   * @param srcImageFile 源图像文件
   * @param destImageFile 目标图像文件
   * @param alpha 透明度：alpha 必须是范围 [0.0, 1.0] 之内（包含边界值）的一个浮点数字
   */
  public static void addImageWaterMark(File waterMark, File srcImageFile, File destImageFile,
      float alpha) {
    try {
      Image src = ImageIO.read(srcImageFile);
      int wideth = src.getWidth(null);
      int height = src.getHeight(null);
      BufferedImage image = new BufferedImage(wideth, height, BufferedImage.TYPE_INT_RGB);
      Graphics2D g = image.createGraphics();
      g.drawImage(src, 0, 0, wideth, height, null);
      // 水印文件
      Image pressImg = ImageIO.read(waterMark);
      int pressImgWidth = pressImg.getWidth(null);
      int pressImgHeight = pressImg.getHeight(null);
      g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
      g.drawImage(pressImg, (wideth - pressImgWidth) / 2, (height - pressImgHeight) / 2,
          pressImgWidth, pressImgHeight,
          null);
      // 水印文件结束
      g.dispose();
      ImageIO.write(image, "JPEG", destImageFile);
    } catch (IOException e) {
      throw new ImageException("图片添加水印图片失败", e);
    }
  }

  /**
   * 计算text的长度（一个中文算两个字符）
   *
   * @param text 文本
   * @return 字符长度，如：text="中国",返回 2；text="test",返回 2；text="中国ABC",返回 4.
   */
  private static int getTextLength(String text) {
    int length = 0;
    for (int i = 0; i < text.length(); i++) {
      if ((text.charAt(i) + "").getBytes().length > 1) {
        length += 2;
      } else {
        length += 1;
      }
    }
    return length / 2;
  }

}
